parceler
parceler
[TOC]
https://github.com/johncarl81/parceler
背景
Parcelable 有很多冗余代码;用 Java 的 Annotation Processor 生成样板代码
基本使用
相关注解
@Parcel
- 如果没有设置
ParcelConverter,那么会用 value - value 默认是
Serialization.FIELD,直接 read/write 字段,Serialization.BEAN通过 setter/getter,
@ParcelConstructor
- 作用于构造器上
- 用于非空构造器,指定该构造器参与 Parceler 的序列化代码生成
@Parcel(Serialization.BEAN)
public class Example {
private final String name;
private final int age;
private boolean enabled;
@ParcelConstructor
public Example(int age, String name, boolean enabled) {
this.age = age;
this.name = name;
this.enabled = enabled;
}
public String getName() { return name; }
public int getAge() { return age; }
public boolean isEnabled() { return enabled; }
}
@ParcelProperty
属性和 getter/setter 混用,未搞明白
@Transient
不需要序列化的字段加上
Parcels 支持的类型
byte
double
float
int
long
char
boolean
String
IBinder
Bundle
SparseArray of any of the mapped types*
SparseBooleanArray
ObservableField
List, ArrayList and LinkedList of any of the mapped types*
Map, HashMap, LinkedHashMap, SortedMap, and TreeMap of any of the mapped types*
Set, HashSet, SortedSet, TreeSet, LinkedHashSet of any of the mapped types*
Parcelable
Serializable
Array of any of the mapped types
Any other class annotated with @Parcel
- 需要泛型的需要指定泛型,不指定就会报错
List testList;
报错
Parceler: Unable to find read/write generator for type java.util.List for me.hacket.assistant.samples.三方库.parcels.Example.testList
单个 Parcelable
// wrap
intent.putExtra(KEY_DATA, Parcels.wrap(parcelModel));
// unwrap
ParcelModel mParcelModel = Parcels.unwrap(intent.getParcelableExtra(KEY_DATA));
集合
对集合类型支持友好
Parcelable listParcelable = Parcels.wrap(new ArrayList<Example>());
Parcelable mapParcelable = Parcels.wrap(new HashMap<String, Example>());
多态 (继承)
https://github.com/johncarl81/parceler#polymorphism
- Parceler 不能 unwrap 子类,所以任何多态字段 unwrap 后都是实例化为其基类实例,为了性能因为这不是运行时,而是编译时,不会检测
.getClass(); - 子类不能继承父类的@Parcel 注解,子类需要单独声明
@Parcel注解
Serialization techniques
注意
- 用
@Parcel注解在要 Parcelable 的类,如果是内部类必须也要用@Parcel修饰 - 如果某个成员存在非
Parcelable的子类的 class,会报错,如View.OnClickListener
Error:(29, 33) 错误: Parceler: Unable to find read/write generator for type android.view.View.OnClickListener for me.hacket.thirdpart.parceler.ParcelModel.mOnClickListener
- 默认序列化策略,字段不能是 private
默认@Parcel,测试是可以的,会反射暴力获取设置值,前提是需要有默认构造器,不建议用 private,用了暴力反射可能会出 bug
- 默认序列化策略,没有空构造器,报错,需要加
@ParcelConstructor注解标记构造器,有且只有一个@ParcelConstructor
No @ParcelConstructor annotated constructor and no default empty bean constructor found.
- 可以为 private,默认构造器,且带 getter/setter
@Parcel(Serialization.BEAN)
public class Example {
private String name;
private int age;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
}
- 报错,可能是该 module 没有应用 kapt 注解处理器
位置: 程序包 club.jinmei.mgvoice.core.arouter.provider.gift
/Users/zengfansheng/Hacket/Workspace/salam/m_room/build/generated/source/kapt/devDebug/club/jinmei/mgvoice/m_room/model/message/RoomGiftMessage$Parcelable.java:86: 错误: 找不到符号
GiftBean giftBean$0 = club.jinmei.mgvoice.core.arouter.provider.gift.GiftBean$Parcelable.read(parcel$3, identityMap$1);
@Transient某个字段不序列化
For attributes that should not be serialized with Parceler, the attribute field, getter or setter may be annotated by @Transient.
非空构造器
@Parcel(Serialization.BEAN)
public class Example {
private final String name;
private final int age;
@ParcelConstructor
public Example(int age, String name) {
this.age = age;
this.name = name;
}
public String getName() { return name; }
public int getAge() { return age; }
}
- 继承关系
https://github.com/johncarl81/parceler#polymorphism
不支持多态接收,接收的总是 Parent。需要自定义。 - 混淆
# Parceler library
-keep interface org.parceler.Parcel
-keep @org.parceler.Parcel class * { *; }
-keep class **$Parcelable { *; }
常见问题
Kotlin 中使用 Parcel
使用 data class 时会报错
Parceler: No @ParcelConstructor annotated constructor and noefault empty bean constructor found.
解决:
1. 将所有的构造参数都设置个默认值
kotlin 的 data class 所有参数都设置个默认值才有空构造器
@Parcel
data class User(
val name: String? = "",
val age: Int = 0,
val g: Boolean = true
)
2. 加上 @ParcelConstructor,这个没测试过
https://github.com/johncarl81/parceler/issues/275
@Parcel
data class User @ParcelConstructor constructor(
val name: String,
val age: Int,
val g: Boolean
)
3. 用 Kotlin 的 @Parcelize
org.parceler.ParcelerRuntimeException: Unable to find generated Parcelable class
org.parceler.ParcelerRuntimeException: Unable to find generated Parcelable class for club.jinmei.mgvoice.m_userhome.model.UserHomeInfo, verify that your class is configured properly and that the Parcelable class club.jinmei.mgvoice.m_userhome.model.UserHomeInfo$Parcelable is generated by Parceler.
类以及加了 @Parcel 注解,但还是报错,这是因为没有添加 kapt 注解编译器
kapt commons["parceler-compiler"]